#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/time.h>
#include <string.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <ctype.h>
#include <sys/errno.h>
#include <sys/stat.h> /* Function to take information from a file */
#include <math.h>
#include <syslog.h>
#include <netdb.h>
#include <fcntl.h>
#include "../Headers/shared.h"


int validArgs (int argc, char * type)
{
    /* Valido Argumentos */
    if (argc != 2 || validType (type) == 0){
        if (argc != 2){
            fprintf (stdout, "Error: Invalid Arguments\nUse: server CONN_TYPE\n");
            return 0;
        }
        else{
            fprintf (stdout, "Error: Invalid type: Use any of the following:\n");
            fprintf (stdout, "TCP | MSGQ | FIFO | SHM\n");
            return 0;
        }
    }
    return 1;
}


/* completar */
int validType (char * type)
{
    return !strcmp (type, "TCP") || !strcmp(type, "MSGQ") || !strcmp(type, "FIFO") || !strcmp(type, "SHM");
}

/* Write "n" bytes to a descriptor. */
/* Stevens */

int
getLine (char * line, int linelen, FILE * stream)
{
    int i=0, c;
    for (i=0;i<linelen;i++)
        line[i] = 0;
    i=0;
    while(i<linelen){
        c=fgetc(stream);
        if(c==EOF)
            return -1;

        if(c=='\n')
            return 1;

        line[i]=c;
        i++;
    }

    if (i >= linelen)
    {
        BORRA_BUFFER;
        return 0;
    }
    return 0;
}

int
writen(int fd, const void *vptr, size_t n)
{
   size_t      nleft;
   ssize_t     nwritten;
   const char  *ptr;

   signal(SIGPIPE, SIG_IGN);
   errno=0;
   ptr = vptr;
   nleft = n;
   while (nleft > 0) {
       if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
           if (nwritten < 0 && errno == EINTR)
               nwritten = 0;       /* and call write() again */
           else
               return(-1);         /* error */
       }
       if(errno == EPIPE)
           return -1;

       nleft -= nwritten;
       ptr   += nwritten;
   }
   return(n);
}
/* end writen */

/* Read "n" bytes from a descriptor. */
/* Stevens */

int
readn(int fd, void *vptr, size_t n)
{
   size_t  nleft;
   ssize_t nread;
   char    *ptr;

   ptr = vptr;
   nleft = n;
   while (nleft > 0) {
       if ( (nread = read(fd, ptr, nleft)) < 0) {
           if (errno == EINTR)
               nread = 0;      /* and call read() again */
           else
               return(-1);
       } else if (nread == 0)
           break;              /* EOF */

       nleft -= nread;
       ptr   += nread;
   }
   return(n - nleft);      /* return >= 0 */
}
/* end readn */

/* stevens */
/* Tiene algunos cambios para poder conectarnos en cierto IP y no todos y los valores de retorno */

unsigned short  portbase = 0;           /* port base, for non-root servers      */

int
passivesock(const char *service, const char *transport, int qlen, char * listen_ip)
/*
 * Arguments:
 *      service   - service associated with the desired port
 *      transport - transport protocol to use ("tcp" or "udp")
 *      qlen      - maximum server request queue length
 */
{
        struct servent  *pse;   /* pointer to service information entry */
        struct protoent *ppe;   /* pointer to protocol information entry*/
        struct sockaddr_in sin; /* an Internet endpoint address         */
        int     s, type;        /* socket descriptor and socket type    */

        memset(&sin, 0, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = (strcmp(listen_ip, "INADDR_ANY")==0) ? INADDR_ANY:inet_addr(listen_ip);
        /* Podramos hacer ac una comparacin con INADDR_ANY y si es igual que escuche en todas
         * sino que escuche en la del archivo */

    /* Map service name to port number */
        if ( pse = getservbyname(service, transport) )
                sin.sin_port = htons(ntohs((unsigned short)pse->s_port)
                        + portbase);
        else if ( (sin.sin_port = htons((unsigned short)atoi(service))) == 0 ){
                fprintf(stdout, "can't get \"%s\" service entry\n", service);
                return 0;
        }

    /* Map protocol name to protocol number */
        if ( (ppe = getprotobyname(transport)) == 0){
                fprintf(stdout, "can't get \"%s\" protocol entry\n", transport);
                return 0;
        }

    /* Use protocol to choose a socket type */
        if (strcmp(transport, "udp") == 0)
                type = SOCK_DGRAM;
        else
                type = SOCK_STREAM;

    /* Allocate a socket */
        s = socket(PF_INET, type, ppe->p_proto);
        if (s < 0){
                fprintf(stdout, "can't create socket: %s\n", strerror(errno));
                return 0;
        }

    /* Bind the socket */
        if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0){
                fprintf(stdout, "can't bind to %s port: %s\n", service,
                        strerror(errno));
                return 0;
        }
        if (type == SOCK_STREAM && listen(s, qlen) < 0){
                fprintf(stdout, "can't listen on %s port: %s\n", service,
                        strerror(errno));
                return 0;
        }
        return s;
}

/* Stevens */

int
passiveTCP(const char *service, int qlen, char * ip)
/*
 * Arguments:
 *      service - service associated with the desired port
 *      qlen    - maximum server request queue length
 */
{
        return passivesock(service, "tcp", qlen, ip);
}

int 
my_max (int a, int b)
{
	return a > b ? a : b;
}

void
putZeros (char * arr, int len)
{
    int i;
    for (i=0; i<len; i++)
        arr[i] = 0;
        
    return ;
}
